package main

import "fmt"

// 定义方法包含
// 方法接收器(receiver)参数、函数/方法参数，以及返回值变了对应的作用域范围，都是函数/方法体对应的显式代码块
type T struct {
	a int
}

/*
// receiver部分的参数名不能与方法参数列表中的形参名，以及具名返回值中的变量名冲突
func (t T) M(t string) { // 编译器报错: duplicate argument t (重复声明参数)

}
*/

// 不过, 如果在方法体中, 我们没有用到receiver参数， 我们也可以省略receiver的参数名, 就像下面这样
func (T) M(t string) {}

// receiver参数的基类型本身不能为指针类型或接口类型
type IntP *int

/**
func (r IntP) String() string { // r 的基类型是IntP, 编译器报错: invalid receiver type IntP (IntP is a pointer type)
	return fmt.Sprintf("%d", *(*int)(r))
}

type MyReader io.Reader

func (r MyReader) Read(p []byte) (int, error) { // r的基类型为Reader, 编译器报错: invalid receiver type MyReader (MyReader is an interface type)
	return r.Read(p)
}
**/
// Go 对方法声明的位置也是有约束的, Go要求, 方法声明要与 receiver 参数的基类型声明放在同一个包内
/**
 * 我们可以得到两个推论：
 * <li>不能为原生类型(诸如int、float64、map等)添加方法。</li>
 * <li>不能跨越Go包为其他包的类型声明新方法。</li>
 **/

func (t T) M0(n int) {}

func (t T) Get() int {
	return t.a
}

func (t *T) Set(a int) int {
	t.a = a
	return t.Get()
}

// receiver参数以第一个参数的身份并入到方法的参数列表中
// 类型T的方法Get的等价
func Get(t T) int {
	return t.a
}

// 类型T得方法Set的等价
func Set(t *T, a int) int {
	t.a = a
	return t.a
}

func main() {
	var t T
	t.M0(1) // 通过类型T得变量实例调用方法M0

	p := &T{}
	p.M0(2) // 通过类型*T的变量实例调用方法M0

	f1 := (*T).Set // f1的类型， 也是 *T 类型Set方法的类型: func (t *T, int) int
	f2 := T.Get    // f2 的类型, 也是 T 类型Get 方法的类型: func(t T) int

	fmt.Printf("the type of f1 is %T\n", f1) // the type of f1 is func(*main.T, int) int
	fmt.Printf("the type of f2 is %T\n", f2) // the type of f2 is func(main.T) int

	f1(&t, 3)
	fmt.Println(f2(t)) // 3
}
